from __future__ import annotations

import logging
import sys
from dataclasses import dataclass
from json import loads, dumps

from school_ringer_modules.config import config_path
from school_ringer_modules.system import run_command
from school_ringer_modules.time_functions import time_before


@dataclass
class Schedule:
    name: str
    pre_call: int
    active: bool
    weekdays: dict
    lessons: dict

    def to_dict(self) -> dict:
        """
        Создаёт словарь из объекта класса
        :return: словарь, полученный преобразованием объекта класса в dict
        """
        return {
            "name": self.name,
            "pre_call": self.pre_call,
            "active": self.active,
            "weekdays": self.weekdays,
            "lessons": self.lessons
        }

    def __repr__(self) -> None:
        """
        Вывод расписания на экран
        """
        try:
            print('\n===================================\nName:', self.name)
            print('Pre-call:', self.pre_call)
            print('Active:', self.active)
            print('Weekdays: ', end='')
            for day in self.weekdays:
                print(day, self.weekdays[day], end=', ')
            print("\nLessons: ", end='')
            for lesson in self.lessons:
                print(lesson, self.lessons[lesson]["start"], self.lessons[lesson]["end"], self.lessons[lesson]["group"],
                      end=', ')

        except Exception:
            pass


class AllSchedules:
    def __init__(self) -> None:
        """
        Базовый конструктор класса, считывает раcписания из файла json
        Если файла нет, он создаётся
        """
        self.filename = f'{config_path}/schedule.json'
        try:
            with open(self.filename, 'r', encoding='utf-8') as file:
                if not file.read():
                    self.clean()
            self.schedules: dict = self._read()
            logging.info(f'Прочитаны из файла расписания: {self.schedules}')
        except FileNotFoundError:
            self.schedules = {
                "Основное":
                    {
                        "name": "Основное",
                        "pre_call": 0,
                        "active": True,
                        "weekdays": {
                            "monday": True,
                            "tuesday": True,
                            "wednesday": True,
                            "thursday": True,
                            "friday": True,
                            "saturday": False,
                            "sunday": False
                        },
                        "lessons":
                            {
                                "1":
                                    {
                                        "start": '09:00',
                                        "end": '09:45',
                                        "group": 'По умолчанию'
                                    }
                            }
                    }
            }
            self._write(self.schedules)
            run_command(f'chmod 644 {self.filename}')
            logging.info(f'Файла с расписаниями не было. Создан новый. Расписания: {self.schedules}')

    def __getitem__(self, item: str) -> Schedule:
        """
        Возвращает объект с расписанием по его имени
        :param item: название расписания
        :return: объект класса Schedule с расписанием с указанным названием
        """
        return Schedule(
            name=self.schedules[item]['name'],
            pre_call=self.schedules[item]['pre_call'],
            active=self.schedules[item]['active'],
            weekdays=self.schedules[item]['weekdays'].copy(),
            lessons=self.schedules[item]['lessons'].copy()

        )

    def __repr__(self) -> None:
        """
        Вывод расписаний на экран
        """
        for schedule in self.schedules:
            self.__getitem__(schedule).__repr__()

    def get_all_schedules_names(self) -> [str]:
        """
        Возвращает список имён всех текущих расписаний
        :return: список строк с названиями расписаний
        """
        return list(self.schedules.keys())

    def get_all_schedules_times(self) -> [str]:
        """
        Возвращает список всех времён звонков из всех расписаний
        @return: список строк с отсортированными временами звонков
        """
        all_times = []
        for schedule in self.schedules:
            for lesson in schedules[schedule].lessons:
                all_times.append(schedules[schedule].lessons[lesson]["start"])
                all_times.append(schedules[schedule].lessons[lesson]["end"])
                while '-' in all_times:
                    all_times.remove('-')
                if schedules[schedule].pre_call != 0:
                    all_times.append(time_before(schedules[schedule].lessons[lesson]["start"],
                                                 schedules[schedule].pre_call))
        return sorted(all_times)

    def _read(self) -> dict:
        """
        Возвращает словарь расписаний, считанный из файла
        Если словаря нет, завершает работу программы
        :return: словарь с расписаниями
        """
        try:
            with open(self.filename, 'r', encoding='utf-8') as file:
                logging.info(f'Расписания считаны из файла {self.filename}')
                return loads(file.read())
        except FileNotFoundError:
            logging.info('[error] Файл ' + self.filename + ' не найден\nАварийная остановка программы')
            sys.exit(0)

    def _write(self, value) -> None:
        """
        Записывает расписания в файл json
        @param value: словарь для записи в файл
        @return: None
        """
        try:
            tempfile = run_command('mktemp').strip()
            with open(tempfile, 'w', encoding='utf-8') as file:
                file.write(dumps(value, indent=4, ensure_ascii=False))
                logging.info(f'Расписания записаны в файл {tempfile}')
            command = f'mv {tempfile} {self.filename} && chmod 644 {self.filename}'
            if run_command('systemctl is-active school-ringer.service').strip() == 'active':
                command += ' && systemctl enable school-ringer.service && systemctl restart school-ringer.service'
            run_command(f'pkexec sh -c "{command}"')
            logging.info(f'Расписания перенесены в {self.filename}')
        except KeyError:
            logging.info('[error] Ошибка записи в файл')
            return None

    def clean(self) -> None:
        """
        Удаляет все расписания, в том числе из файла json
        """
        value = {}
        try:
            with open(self.filename, 'w', encoding='utf-8') as file:
                file.write(dumps(value))
            self.schedules: dict = self._read()
            logging.info(f'Удалены все расписания')
        except KeyError:
            logging.info('[error] Ключ не найден')
            return None

    def __setitem__(self, key: str, new_schedule: Schedule):
        """
        Устанавливает расписание new_schedule по ключу key в словаре всех расписаний
        :param key: название расписания
        :param new_schedule: объект класса Schedule, содержащий расписание для добавления
        :return: все расписания с добавленным
        """
        logging.info(f"Добавлено новое расписание {new_schedule.name} = {new_schedule.to_dict()} без записи в файл")
        self.schedules[key] = new_schedule.to_dict()
        return self

    def __delitem__(self, key: str) -> AllSchedules:
        """
        Удаление расписания по ключу
        :param key: название расписания
        :return: все расписания без удалённого
        """
        del self.schedules[key]
        logging.info(f'Удалено расписание {key}')
        return self

    def save(self) -> AllSchedules:
        """
        Сохранение расписаний в файл
        @return: все расписания
        """
        print('Saving...')
        self._write(self.schedules)
        logging.info(f'Расписания {self.schedules.keys()} сохранены в файл')
        return self

    def open_read_from_file(self) -> AllSchedules:
        """
        Считывание расписаний из файла
        @return: все расписания, считанные из файла
        """
        self.schedules = self._read()
        return self


schedules = AllSchedules()
